const Redis = require("redis");
const { redis } = require("../utils/config");
const client = Redis.createClient({
  host: redis.host,
  port: redis.port,
  no_ready_check: true,
  retry_strategy: function (options) {
    if (options.error.code === "ECONNREFUSED") {
      // End reconnecting on a specific error and flush all commands with a individual error
      report("[redis]", "connection not permitted.");
    }
    if (options.times_connected > 10) {
      report("[redis]", "Retry the connection more than ten times.");
    }
    // reconnect after
    return Math.max(options.attempt * 100, 3000);
  },
});
client.connect();
client.on("connect", function () {
  console.log("[redis]", "connect ok.");
});
client.on("error", (err) => {
  console.error("[redis]", err);
});
client.on("monitor", function (time, args) {
  console.log("[redis]", time + ": " + util.inspect(args));
});

const radisGet = (key, bool) => {
  return new Promise((resolve, reject) => {
    console.log("[redis]", "[get]", key);
    Promise.all([radisHas(key)])
      .then((res) => {
        res[0]
          ? client
              .get(key)
              .then((res) => {
                try {
                  resolve(bool ? res : JSON.parse(res));
                } catch (err) {
                  reject(err);
                }
              })
              .catch((err) => reject(err))
          : resolve(undefined);
      })
      .catch((err) => reject(err));
  });
};

const radisSet = (key, val, bool, timeout) => {
  return new Promise((resolve, reject) => {
    try {
      if (typeof val === "object") {
        val = JSON.stringify(val);
      } else if (typeof val === "undefined") {
        reject({ err: "value no undefined" });
        return;
      }

      console.log("[redis]", "[set]", key);
      Promise.all([radisHas(key)])
        .then((res) => {
          if (!res[0]) {
            client
              .set(key, val)
              .then((res) => {
                resolve(res);
              })
              .catch((err) => reject(err));
          } else {
            let tasks = [client.set(key, val)];
            if (bool) tasks.unshift(radisDelete(key));
            Promise(tasks)
              .then((res) => {
                resolve(res[1]);
              })
              .catch((err) => reject(err));
          }
        })
        .catch((err) => reject(err));
      client.expire(key, timeout || 1 * 1000 * 60 * 60 * 24 * 30 * 12);

      client.send_command(
        "config",
        ["set", "notify-keyspace-events", "Ex"],
        SubscribeExpired
      );

      function SubscribeExpired(err, res) {
        if (err) {
          return;
        }
        const expired_subKey = "__keyevent@0__:expired"; //订阅key过期事件
        client.subscribe(expired_subKey, function () {
          client.on("message", function (chan, msg) {
            console.log("[radis]", "[message]", "chan:" + chan);
            //判断key做出相应的处理。
            console.log("[radis]", "[message]", "msg:" + msg);
          });
        });
      }
    } catch (err) {
      reject(err);
    }
  });
};

const radisDelete = (key) => {
  return new Promise((resolve, reject) => {
    console.log("[redis]", "[del]" + key);
    Promise.all([radisHas(key)])
      .then((res) => {
        if (res[0]) {
          client
            .del(key)
            .then((res) => {
              console.log("[redis]", "[del]", key, "res " + res);
              resolve(res);
            })
            .catch((err) => reject(err));
        }
      })
      .catch((err) => reject(err));
  });
};

const radisHas = (key) => {
  return new Promise((resolve, reject) => {
    console.log("[redis]", "[has]", key);
    Promise.all([client.ping(), client.exists(key)])
      .then((res) => {
        console.log("[redis]", "[ping]", key, "value " + res[0]);
        console.log("[redis]", "[has]", key, "value " + res[1]);
        resolve(res[1]);
      })
      .catch((err) => reject(err));
  });
};

//client.auth(123456); // 如果没有设置密码 是不需要这一步的
module.exports = {
  client,
  get: radisGet,
  set: radisSet,
  has: radisHas,
  del: radisDelete,
};
